package de.herzog.bbp.controller;

import static de.herzog.bbp.view.SpielBeanAutoCompleter.DATE_LIST;
import static de.herzog.bbp.view.SpielBeanAutoCompleter.HALLE_LIST;
import static de.herzog.bbp.view.SpielBeanAutoCompleter.KLASSE_LIST;
import static de.herzog.bbp.view.SpielBeanAutoCompleter.MANNSCHAFT_LIST;
import static de.herzog.bbp.view.SpielBeanAutoCompleter.ZEIT_LIST;
import static de.herzog.bbp.view.SpielBeanConverter.DATE_CONVERTER;
import static de.herzog.bbp.view.SpielBeanConverter.HALLE_CONVERTER;
import static de.herzog.bbp.view.SpielBeanConverter.KLASSE_CONVERTER;
import static de.herzog.bbp.view.SpielBeanConverter.MANNSCHAFT_CONVERTER;
import static de.herzog.bbp.view.SpielBeanValidator.DATE_VALIDATOR;
import static de.herzog.bbp.view.SpielBeanValidator.HALLE_VALIDATOR;
import static de.herzog.bbp.view.SpielBeanValidator.KLASSE_VALIDATOR;
import static de.herzog.bbp.view.SpielBeanValidator.MANNSCHAFT_VALIDATOR;
import static de.herzog.bbp.view.SpielBeanValidator.ZEIT_VALIDATOR;

import java.util.Date;
import java.util.List;
import java.util.stream.Stream;

import javafx.application.Platform;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import javafx.collections.transformation.FilteredList;
import javafx.concurrent.Task;
import javafx.fxml.FXML;
import javafx.scene.control.ComboBox;
import javafx.scene.control.Label;
import javafx.scene.control.TableColumn;
import javafx.scene.control.TableView;
import javafx.scene.control.TextArea;
import javafx.scene.control.cell.PropertyValueFactory;
import de.herzog.bbp.model.Halle;
import de.herzog.bbp.model.Klasse;
import de.herzog.bbp.model.Mannschaft;
import de.herzog.bbp.model.Spiel;
import de.herzog.bbp.model.Verein;
import de.herzog.bbp.model.dao.KlasseDAO;
import de.herzog.bbp.model.dao.MannschaftDAO;
import de.herzog.bbp.model.dao.SpielDAO;
import de.herzog.bbp.view.SpielBeanSorter;
import de.herzog.bbp.view.bean.SpielBean;
import de.herzog.bbp.view.javafx.AbstractStringConverter;
import de.herzog.bbp.view.javafx.AutoCompleteListFactory;
import de.herzog.bbp.view.javafx.EditingTableCell;
import de.herzog.bbp.view.javafx.SaveOnSelectionChangedListener;
import de.herzog.bbp.view.javafx.Validator;

public class SpieleTableController {

	public static final String ZEIT_COLUMN = "zeit";
	public static final String HEIM_COLUMN = "heim";
	public static final String GAST_COLUMN = "gast";

	private static final String FILTER_ALL = "-Alle-";

	private ObservableList<SpielBean> beanList;
	private FilteredList<SpielBean> filteredList;

	@FXML
	private ComboBox<String> klasseFilterComboBox;
	@FXML
	private ComboBox<String> heimvereinFilterComboBox;
	@FXML
	private ComboBox<SpielBeanSorter> sorterComboBox;
	@FXML
	private TableView<SpielBean> spieleTableView;
	@FXML
	private TableColumn<SpielBean, Klasse> klasseColumn;
	@FXML
	private TableColumn<SpielBean, Integer> nummerColumn;
	@FXML
	private TableColumn<SpielBean, String> wochentagColumn;
	@FXML
	private TableColumn<SpielBean, Date> datumColumn;
	@FXML
	private TableColumn<SpielBean, String> zeitColumn;
	@FXML
	private TableColumn<SpielBean, Mannschaft> heimColumn;
	@FXML
	private TableColumn<SpielBean, Mannschaft> gastColumn;
	@FXML
	private TableColumn<SpielBean, Halle> halleColumn;
	@FXML
	private TableColumn<SpielBean, String> schiriColumn;
	@FXML
	private TableColumn<SpielBean, String> errorColumn;
	@FXML
	private TextArea messageArea;

	public SpieleTableController() {
	}

	@FXML
	private void initialize() {
		final boolean editable = true;
		initializeFilter(klasseFilterComboBox, () -> KlasseDAO.loadAll().stream().map(KLASSE_CONVERTER::toString));
		initializeFilter(heimvereinFilterComboBox, () -> MannschaftDAO.loadAll().stream().map(Mannschaft::getVerein)
				.map(Verein::getName).distinct());
		initializeSorter();
		initializeTableView(editable);
		// klasseColumn.initializeColumn(propertyName, editable);
		initializeColumn(klasseColumn, "klasse", editable, KLASSE_VALIDATOR, KLASSE_CONVERTER, KLASSE_LIST);
		initializeColumn(nummerColumn, "nummer", !editable, null, null, null);
		initializeColumn(wochentagColumn, "wochentag", !editable, null, null, null);
		initializeColumn(datumColumn, "datum", editable, DATE_VALIDATOR, DATE_CONVERTER, DATE_LIST);
		initializeColumn(zeitColumn, ZEIT_COLUMN, editable, ZEIT_VALIDATOR, null, ZEIT_LIST);
		initializeColumn(heimColumn, HEIM_COLUMN, editable, MANNSCHAFT_VALIDATOR, MANNSCHAFT_CONVERTER, MANNSCHAFT_LIST);
		initializeColumn(gastColumn, GAST_COLUMN, editable, MANNSCHAFT_VALIDATOR, MANNSCHAFT_CONVERTER, MANNSCHAFT_LIST);
		initializeColumn(halleColumn, "halle", editable, HALLE_VALIDATOR, HALLE_CONVERTER, HALLE_LIST);
		initializeColumn(schiriColumn, "schiri", !editable, null, null, null);
		initializeColumn(errorColumn, "error", !editable, null, null, null);
	}

	@FunctionalInterface
	private interface StringStreamFactory {
		Stream<String> getStream();
	}

	private void initializeFilter(ComboBox<String> comboBox, StringStreamFactory stringStreamFactory) {
		comboBox.setItems(FXCollections.observableArrayList(FILTER_ALL));
		comboBox.setValue(FILTER_ALL);
		comboBox.focusedProperty().addListener((obs, oldFocus, newFocus) -> {
			if (newFocus && !oldFocus) {
				ObservableList<String> list = FXCollections.observableArrayList(FILTER_ALL);
				stringStreamFactory.getStream().sorted().forEach(list::add);
				comboBox.setItems(list);
			}
		});
		comboBox.setOnAction(event -> {
			filteredList.setPredicate(spielBean -> {
				boolean klasseAll = klasseFilterComboBox.getValue().equals(FILTER_ALL);
				boolean klasseMatch = klasseFilterComboBox.getValue().equals(spielBean.getKlasse());
				boolean heimVereinAll = heimvereinFilterComboBox.getValue().equals(FILTER_ALL);
				boolean heimvereinMatch = heimvereinFilterComboBox.getValue().equals(spielBean.getHeimVerein());
				return (klasseAll || klasseMatch) && (heimVereinAll || heimvereinMatch);
			});
		});
	}

	private void initializeSorter() {
		sorterComboBox.getItems().add(SpielBeanSorter.KLASSE_SORT);
		sorterComboBox.getItems().add(SpielBeanSorter.HEIMVEREIN_SORT);
		sorterComboBox.setValue(SpielBeanSorter.KLASSE_SORT);
		sorterComboBox.setOnAction(event -> sorterComboBox.getValue().sort(beanList));
	}

	private void initializeTableView(boolean editable) {
		spieleTableView.setPlaceholder(new Label("Daten werden geladen..."));
		Task<Object> loadTask = new Task<Object>() {
			@Override
			protected Object call() throws Exception {
				Platform.runLater(new Runnable() {
					@Override
					public void run() {
						List<Spiel> modelList = SpielDAO.loadAll();
						if (modelList.size() == 0) {
							Spiel spiel = new Spiel();
							spiel.setSchiri("XXXX");
							modelList.add(spiel);
						}
						beanList = FXCollections.observableArrayList();
						modelList.stream().map(SpielBean::new).forEach(beanList::add);
						List<String> messages = SpieleCheck.check(beanList, SpieleCheck.Mode.ALL);
						showMessages(messages);
						SpielBeanSorter.KLASSE_SORT.sort(beanList);
						filteredList = new FilteredList<SpielBean>(beanList, p -> true);

						spieleTableView.setItems(filteredList);
						SaveOnSelectionChangedListener<SpielBean> listener = new SaveOnSelectionChangedListener<SpielBean>(
								spieleTableView.getItems());
						spieleTableView.getSelectionModel().getSelectedIndices().addListener(listener);
						spieleTableView.setEditable(editable);
					}
				});
				return null;
			}
		};
		Thread loadThread = new Thread(loadTask);
		loadThread.setDaemon(true);
		loadThread.start();
	}

	private <T> void initializeColumn(TableColumn<SpielBean, T> column, final String propertyName, boolean editable,
			Validator validator, AbstractStringConverter<SpielBean, T> converter,
			AutoCompleteListFactory<SpielBean, T> autoCompleteListFactory) {
		column.setId(propertyName);
		column.setEditable(editable);
		column.setCellValueFactory(new PropertyValueFactory<SpielBean, T>(propertyName));
		if (editable) {
			column.setCellFactory(callback -> new EditingTableCell<>(validator, converter, autoCompleteListFactory));
		}
	}

	public void checkCount() {
		List<String> messages = SpieleCheck.check(beanList, SpieleCheck.Mode.KLASSEN);
		repaintTableView();
		showMessages(messages);
	}

	public void checkHallenDoppel() {
		List<String> messages = SpieleCheck.check(beanList, SpieleCheck.Mode.HALLEN);
		repaintTableView();
		showMessages(messages);
	}

	private void repaintTableView() {
		spieleTableView.getColumns().get(0).setVisible(false);
		spieleTableView.getColumns().get(0).setVisible(true);
	}

	private void showMessages(List<String> errorMessages) {
		if (errorMessages.size() > 0) {
			messageArea.setText("FEHLER:");
			for (String message : errorMessages) {
				messageArea.appendText("\n");
				messageArea.appendText(message);
			}
		} else {
			messageArea.setText("OK");
		}
	}

	public void save() {
	}
}
